今天我要來分享方法(Method)的概念,方法有分三種:
我將昨日的程式碼做一個小小的修改,我將所有的需要引入的參數都刪除,只留下了self,讓我們看一下底下這段程式碼:
class Person(object):
def introduce_myself(self):
print("Hi, I am {self} ".format(self=self))
if __name__ == '__main__':
Jacky = Person()
Jacky.introduce_myself() #Hi, I am <__main__.Person object at 0x10cb271d0>
你是否不禁好奇,為什麼留了一個「self」的參數呢?讓我們試著把這個self印出一探究竟,我們可以發現這個self印出的是Personal的物件以及記憶體位置。
我將程式碼在做一個小小的修改,如果我們沒有先實例化一個物件會變成怎樣呢?,讓我們看一下底下這段程式碼:
class Person(object):
def introduce_myself(self):
print("Hi, I am {self} ".format(self=self))
self.ten_years_later()
def ten_years_later(self):
print("Hi, I am {self} ".format(self=self))
if __name__ == '__main__':
# 有先實例化物件
Jacky = Person()
Jacky.introduce_myself() #Hi, I am <__main__.Person object at 0x100bd7278> *2
Sandy = Person()
Sandy.introduce_myself() #Hi, I am <__main__.Person object at 0x100bd72b0> *2
# 沒有實例化物件
Person.introduce_myself() #TypeError: introduce_myself() missing 1 required positional argument: 'self'
從15行我們可以發現,如果我們沒有實例化的話,會出現
TypeError: introduce_myself() missing 1 required positional argument: 'self'
的錯誤,這個錯誤是說缺少一個self的參數,也就是説在呼叫方法的同時,這個物件會作為參數傳入方法,所以一定必須得要先實例化一個物件才能夠呼叫方法,這也就是我們所說的實體方法。
第5行我們能看到因為實體方法是用把實例代入參數裡面,所以Jacky & Sandy這兩個「物件」的所有方法,都能夠透過「self」互相呼叫非常的方便。最後我們可以發現實例方法的英文裡面有「Instance」,從英文上去理解能夠比較好理解,其實實體方法就是實例(Instance) 的方法(Method),底下要介紹的類別方法也是一樣的概念。
其實每一種方法,都很相似,我們馬上來看看類別方法例子的程式碼:
class Person(object):
@classmethod
def jacky_introduce_myself(cls):
print("Hi, I am Jacky {cls} ".format(cls=cls)) # Hi, I am Jacky <class '__main__.Person'>
cls.sandy_introduce_myself()
@classmethod
def sandy_introduce_myself(cls):
print("Hi, I am Sandy {cls} ".format(cls=cls)) # Hi, I am Sandy <class '__main__.Person'>
if __name__ == '__main__':
# 有先實例化物件
Jacky = Person()
Jacky.jacky_introduce_myself()
print(Jacky) # <__main__.Person object at 0x106ad12e8>
# 沒有實例化物件
Person.jacky_introduce_myself() # Hi, I am Jacky <class '__main__.Person'> & Hi, I am Sandy <class '__main__.Person'>
我在兩個方法上面各加上一行@classmethod
這段程式碼使他變成類別方法,然後把「self」換成「cls」然後接著印出,我們可以發現「cls指向的是Person這個類別」,而不是像實體方法「self是指向類別實例化後的物件」,所以我們在第6行可以發現,Person這個類別也能透過「cls」互相呼叫,然後類別方法無論有沒有實例化物件都是能夠呼叫。
接著我們要來介紹靜態方法,一樣很快地看到我們的範例程式碼:
class Person(object):
@staticmethod
def jacky_introduce_myself():
print("Hi, I am Jacky ") # Hi, I am Jacky
Person.sandy_introduce_myself()
@staticmethod
def sandy_introduce_myself():
print("Hi, I am Sandy ") # Hi, I am Sandy
if __name__ == '__main__':
# 有先實例化物件
Jacky = Person()
Jacky.jacky_introduce_myself()
print(Jacky) # <__main__.Person object at 0x106ad12e8>
# 沒有實例化物件
Person.jacky_introduce_myself() # Hi, I am Jacky & Hi, I am Sandy
靜態方法的範例程式碼跟類別的也太像了,只差在參數「cls」刪掉以及@classmethod
改成了@staticmethod
,看似簡單的動作,少了cls這個參數雖然一樣能夠呼叫類別底下的方法,但是在懂的人眼中架構卻是大大的不一樣。靜態方法更像是一個很單純的工具,沒有物件的架構,只是可能常常會被別人拿來用,所以宣告靜態的方式,不需要其他的東西。
最後總結之前我來提一下函式和方法的差別,「函式只是程式碼組成的區塊,他可以寫在任何地方,無論是類別裡面或者是類別外面都可以」,但是方法就不一樣了,「方法都是基於類別,有一個隱性、預設的參數(self or cls),如果你不是寫在類別裡面,呼叫方法的時候,一樣會有缺少self or 缺少cls 參數的錯誤」。
題外話這些參數可以不使用這些名稱(self or cls),但是因為大家使用上這樣稱呼,也容易看懂,久而久之就習慣成自然了。
最後來個總結一下今天介紹的三種方法,雖然每種方法都有用有實體物件與沒有實體物件,但是實際上只要記得實體物件對實體方法十分地重要,另外兩種方法可以不需要。
實體方法(Instance Method) | 類別方法(Class Method) | 靜態方法(Static Method) | |
---|---|---|---|
呼叫方式 | 實體物件 | 類別 | 類別 |
傳入參數 | self | cls | 無 |
明日我會把今日分享的方法(Method)搭配屬性(Attribute) 做一個更完整的說明。